// CGraph.FBX.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "CGraph.FBX.h"


#ifdef _MANAGED
#pragma managed(push, off)
#endif

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
    return TRUE;
}

#ifdef _MANAGED
#pragma managed(pop)
#endif

using namespace std;

namespace CGraph { namespace FBX {

	class Property
	{
	public:
		virtual ~Property(){}
		virtual const char* GetName() const { return ""; }
		virtual EFbxType GetType() const { return (EFbxType)0; }
	};
	class FbxPropertyWrapper : public Property
	{
		KFbxProperty m_prop;
	public:
		FbxPropertyWrapper( KFbxProperty prop ) : m_prop( prop ){}
		virtual const char* GetName() const { return m_prop.GetName(); }
		virtual EFbxType GetType() const { return m_prop.GetPropertyDataType().GetType(); }
		const KFbxProperty& GetProperty() { return m_prop; }
	};
	class MatrixProperty : public Property
	{
		KFbxMatrix m_matrix;
		string m_name;
	public:
		MatrixProperty( KFbxMatrix matrix, const string& name ) : m_matrix( matrix ), m_name( name ){}
		virtual const char* GetName() const { return m_name.c_str(); }
		virtual EFbxType GetType() const { return eDOUBLE44; }
		const KFbxMatrix& GetMatrix() { return m_matrix; }
	};
	class StringProperty : public Property
	{
		KString m_value;
		string m_name;
	public:
		StringProperty( const KString& value, const string& name ) : m_value( value ), m_name( name ){}
		virtual const char* GetName() const { return m_name.c_str(); }
		virtual EFbxType GetType() const { return eSTRING; }
		const KString& GetString() { return m_value; }
	};

	template< typename T > 
	inline uint32 IndexOf( const T& value, const std::vector<T>& data )
	{
		std::vector<T>::const_iterator find = std::find( data.begin(), data.end(), value );
		if ( find != data.end() )
			return (uint32)(find - data.begin());
		return 0;
	}

	struct GeometryDataBuffer
	{
		uint32 size; //in floats
		uint32 stride; // in floats
		double* data;
	};

	static inline GeometryDataBuffer CreateGeometryDataBuffer( uint32 size, uint32 stride, double* data )
	{
		GeometryDataBuffer buffer;
		buffer.size = size;
		buffer.stride = stride;
		buffer.data = data;
		return buffer;
	}
	typedef std::vector<uint32> TIndexList;
	typedef std::vector<TIndexList> TIndexListList;

	struct GeometryIndexBufferTextures
	{
		TIndexList textures;
		uint32 blendMode;
		double alpha;
	};

	struct GeometryIndexBuffer
	{
		uint32 size;
		uint32 dataBuffer;
		uint32* indexes;
		GeometryIndexBufferTextures textures;
		EGeometryIndexBufferType type;
		KFbxLayerElement::EMappingMode mappingMode;
	};

	static inline GeometryIndexBuffer CreateGeometryIndexBuffer( uint32 size, uint32 dataBuffer, uint32* indexes, EGeometryIndexBufferType type, KFbxLayerElement::EMappingMode mappingMode )
	{
		GeometryIndexBuffer buffer;
		buffer.size = size;
		//Index into the data buffers
		buffer.dataBuffer = dataBuffer;
		//Index into the index buffers
		buffer.indexes = indexes;
		buffer.type = type;
		buffer.textures.alpha = 0;
		buffer.textures.blendMode = 0;
		buffer.mappingMode = mappingMode;
		return buffer;
	}

	struct GeometryInfo
	{
		typedef vector<GeometryDataBuffer> TGeometryDataBufferList;
		typedef vector<GeometryIndexBuffer> TGeometryIndexBufferList;
		TIndexListList m_indexLists;
		TGeometryDataBufferList m_dataBuffers;
		TGeometryIndexBufferList m_indexBuffers;
	};

	static inline uint32 TransferArray( KFbxLayerElementArrayTemplate<KFbxVector2>* data, GeometryInfo::TGeometryDataBufferList& buffers )
	{
		if ( data == NULL )
			return 0;
		uint32 retval = (uint32)buffers.size();
		buffers.push_back( CreateGeometryDataBuffer( data->GetCount()*2, 2, (double*)data->GetLocked() ) );
		return retval;
	}
	static inline uint32 TransferArray( KFbxLayerElementArrayTemplate<KFbxVector4>* data, GeometryInfo::TGeometryDataBufferList& buffers )
	{
		if ( data == NULL )
			return 0;
		uint32 retval = (uint32)buffers.size();
		buffers.push_back( CreateGeometryDataBuffer( data->GetCount()*4, 4, (double*)data->GetLocked() ) );
		return retval;
	}

	template<typename T>
	static inline GeometryIndexBuffer& TransferArrays( T* data, KFbxLayerElementArrayTemplate<int>* indexes, EGeometryIndexBufferType bufferType, KFbxLayerElement::EMappingMode mappingMode, GeometryInfo& result )
	{
		uint32 geomBuffer = 0;
		if ( data )
			geomBuffer = TransferArray( data, result.m_dataBuffers );
		uint32* indexBuffer = NULL;
		uint32 indexSize = 0;
		if ( indexes )
		{
			indexBuffer = (uint32*)indexes->GetLocked();
			indexSize = (uint32)indexes->GetCount();
		}
		result.m_indexBuffers.push_back( CreateGeometryIndexBuffer( indexSize, geomBuffer, indexBuffer, bufferType, mappingMode ) );
		return result.m_indexBuffers.back();
	}

	static inline void TransferLayerTextureElement( GeometryInfo& values, KFbxLayerElementTexture* texture, KFbxLayerElementUV* uv, EGeometryIndexBufferType bufferType, std::vector<KFbxTexture*>& sceneTextures )
	{
		if ( uv == NULL )
			return;
		GeometryIndexBuffer& indexBuffer = TransferArrays( uv->mDirectArray, uv->mIndexArray, bufferType, uv->GetMappingMode(), values );
		if ( texture )
		{
			indexBuffer.textures.alpha = texture->GetAlpha();
			indexBuffer.textures.blendMode = texture->GetBlendMode();
			for ( int index = 0; index < texture->mDirectArray->GetCount(); ++index )
			{
				uint32 textureIndex = IndexOf( texture->mDirectArray->GetAt( index ), sceneTextures );
				if ( textureIndex != 0 )
					indexBuffer.textures.textures.push_back( textureIndex );
			}
		}
	}
	//Very minimal mappings under the assumption that it is more efficient to
	//work in fsharp than in cpp.  So doing the least possible in cpp.
	class Context
	{
	public:

		typedef vector<Property*> TPropertyList;
		typedef vector<TPropertyList> TPropertyListList;
		//So I can augment the properties with more if I need to.
		typedef map<KFbxObject*, uint32> TPropertyMap;
		typedef vector<KFbxNode*> TNodeList;
		typedef pair<uint32,uint32> TEdge;
		typedef vector<TEdge> TEdgeList;
		typedef vector<KFbxMesh*> TMeshList;
		typedef vector<KFbxCamera*> TCameraList;
		typedef vector<KFbxLight*> TLightList;
		typedef vector<KFbxTexture*> TTextureList;
		typedef vector<KFbxSurfaceMaterial*> TMaterialList;
		typedef map<KFbxMesh*, GeometryInfo> TGeometryDataMap;

		TPropertyListList m_properties;
		TPropertyMap m_propMap;

		TNodeList m_nodes;
		TEdgeList m_edges;
		TMeshList m_meshes;
		TGeometryDataMap m_meshData;
		TCameraList m_cameras;
		TLightList m_lights;
		TTextureList m_textures;
		TMaterialList m_materials;

		string m_errorMessage;
		KFbxSdkManager* gSdkManager;
		KFbxImporter* gImporter;
		KFbxScene* gScene;

		Context( const Context& other );
		Context& operator=( const Context& other );

		Context() 
			: gSdkManager( NULL )
			, gImporter (NULL )
			, gScene( NULL )
		{}

		virtual ~Context()
		{
			Terminate();
		}
		
		virtual void Terminate()
		{
			if ( gSdkManager )
			{
				gSdkManager->Destroy();
				gSdkManager = NULL;
			}
			for ( unsigned index = 0; index < m_properties.size(); ++index )
				for ( unsigned j = 0; j < m_properties[index].size(); ++j )
					delete m_properties[index][j];

			m_properties.clear();
			m_propMap.clear();
			m_nodes.clear();
			m_edges.clear();
			m_meshes.clear();
			m_cameras.clear();
			m_lights.clear();
			m_textures.clear();
			m_materials.clear();
			m_meshData.clear();

			/// Push back dummy objects.  0 is never a valid index.
			m_properties.push_back( TPropertyList() );
			m_meshes.push_back( NULL );
			m_cameras.push_back( NULL );
			m_lights.push_back( NULL );
			m_nodes.push_back( NULL );
			m_edges.push_back( std::make_pair( 0U, 0U ) );
			m_textures.push_back( NULL );
			m_materials.push_back( NULL );
		}

		virtual void LoadFile( const charType* gFileName )
		{
			Terminate();
			// Create the importer.
			int lFileFormat = -1;

			gImporter = KFbxImporter::Create(gSdkManager,"");
			if (!gSdkManager->GetIOPluginRegistry()->DetectFileFormat(gFileName, lFileFormat) )
			{
				// Unrecognizable file format. Try to fall back to KFbxImporter::eFBX_BINARY
				lFileFormat = gSdkManager->GetIOPluginRegistry()->FindReaderIDByDescription( "FBX binary (*.fbx)" );;
			}
			gImporter->SetFileFormat(lFileFormat);

			// Create the scene.
			gScene = KFbxScene::Create(gSdkManager,"");
			if ( gImporter->Initialize(gFileName) == false )
				m_errorMessage = gImporter->GetLastErrorString();
			else
			{
				gImporter->Import(gScene);
				KFbxNode* node = gScene->GetRootNode();
				node->ConvertPivotAnimation(KFbxNode::eSOURCE_SET, 30);

				KArrayTemplate<KFbxSurfaceMaterial*> materials;
				KArrayTemplate<KFbxTexture*> textures;
				KArrayTemplate<KFbxNode*> nodes;
				gScene->FillMaterialArray( materials );
				gScene->FillTextureArray( textures );

				m_materials.reserve( materials.GetCount() );
				m_textures.reserve( textures.GetCount() );

				for ( int index = 0; index < materials.GetCount(); ++index )
					m_materials.push_back( materials[index] );


				for ( int index = 0; index < textures.GetCount(); ++index )
					m_textures.push_back( textures[index] );

				ProcessNode( node );
			}
		}

		uint32 ProcessNode( KFbxNode* node )
		{
			uint32 nodeIndex = (uint32)m_nodes.size();
			m_nodes.push_back( node );
			KFbxMesh* mesh = node->GetMesh();
			if ( mesh != NULL )
				m_meshes.push_back( mesh );
			KFbxCamera* camera = node->GetCamera();
			if ( camera != NULL )
				m_cameras.push_back( camera );
			KFbxLight* light = node->GetLight();
			if ( light != NULL )
				m_lights.push_back( light );

			KFbxMatrix global = node->GetGlobalFromDefaultTake();
			KFbxObject* obj = node;
			uint32 propIndex = AddObjectProperties( obj );

			m_properties[propIndex].push_back( new MatrixProperty( global, "DefaultGlobalTransform" ) );
			m_properties[propIndex].push_back( new StringProperty( node->GetName(), "NodeName" ) );
			
			for ( int index = 0; index < node->GetChildCount(); ++index )
			{
				uint32 childIndex = ProcessNode( node->GetChild(index) );
				m_edges.push_back( make_pair( nodeIndex, childIndex ) );
			}
			return nodeIndex;
		}
	
		void ProcessMesh( KFbxMesh* mesh )
		{
			//if already processed.
			if ( m_meshData.find( mesh ) != m_meshData.end() )
				return;
			m_meshData[mesh] = GeometryInfo();
			GeometryInfo& values = m_meshData[mesh];
			//Control points are always the first geometry buffer.
			values.m_dataBuffers.push_back( CreateGeometryDataBuffer( (uint32)mesh->GetControlPointsCount()*4, 4, (double*)mesh->GetControlPoints() ) );
			values.m_indexLists.push_back( TIndexList() );
			values.m_indexLists.push_back( TIndexList() );
			TIndexList& indexes = values.m_indexLists[0];
			TIndexList& counts = values.m_indexLists[1];
			int lPolygonCount = mesh->GetPolygonCount();
			for ( int poly = 0; poly < lPolygonCount; ++poly )
			{
				int lVerticeCount = mesh->GetPolygonSize(poly);
				counts.push_back( (uint32)lVerticeCount );
				for (int lVerticeIndex = 0; lVerticeIndex < lVerticeCount; lVerticeIndex++)
					indexes.push_back( (uint32)mesh->GetPolygonVertex(poly, lVerticeIndex) );
			}

			values.m_indexBuffers.push_back( CreateGeometryIndexBuffer( (uint32)indexes.size(), 0, &(indexes[0]), EGeometryIndexBufferTypePolygonVertex, KFbxLayerElement::eBY_CONTROL_POINT ) );
			values.m_indexBuffers.push_back( CreateGeometryIndexBuffer( (uint32)counts.size(), 0, &(counts[0]), EGeometryIndexBufferTypePolygonCount, KFbxLayerElement::eBY_CONTROL_POINT ) );

			KFbxLayer* layer = mesh->GetLayer(0);
			if ( layer )
			{
				KFbxLayerElementNormal* normals = layer->GetNormals();
				if ( normals )
					TransferArrays( normals->mDirectArray, normals->mIndexArray, EGeometryIndexBufferTypeNormal, normals->GetMappingMode(), values );

				TransferLayerTextureElement( values, layer->GetDiffuseTextures(),	layer->GetDiffuseUV(),		EGeometryIndexBufferTypeDiffuse,	m_textures );
				TransferLayerTextureElement( values, layer->GetAmbientTextures(),	layer->GetAmbientUV(),		EGeometryIndexBufferTypeAmbient,	m_textures );
				TransferLayerTextureElement( values, layer->GetEmissiveTextures(),	layer->GetEmissiveUV(),		EGeometryIndexBufferTypeEmissive,	m_textures );
				TransferLayerTextureElement( values, layer->GetSpecularTextures(),	layer->GetSpecularUV(),		EGeometryIndexBufferTypeSpecular,	m_textures );
				TransferLayerTextureElement( values, layer->GetNormalMapTextures(), layer->GetNormalMapUV(),	EGeometryIndexBufferTypeNormalMap,	m_textures );
				TransferLayerTextureElement( values, layer->GetBumpTextures(),		layer->GetBumpUV(),			EGeometryIndexBufferTypeBumpMap,	m_textures );
			}
		}

		uint32 AddObjectProperties( KFbxObject* object )
		{
			TPropertyMap::iterator find = m_propMap.find( object );
			if ( find != m_propMap.end() )
				return find->second;
			uint32 retval = (uint32)m_properties.size();
			m_properties.push_back( TPropertyList() );
			m_propMap[object] = retval;
			for (KFbxProperty prop = object->GetFirstProperty(); prop.IsValid(); prop = object->GetNextProperty( prop ) )
				m_properties[retval].push_back( new FbxPropertyWrapper( prop ) );
			return retval;
		}
	};
}}

using namespace CGraph::FBX;
	
int32 CGraphFBXCreateParseContext()
{
	return (int32) new Context();
}
inline Context* GetContext( int32 val ) { return reinterpret_cast<Context*>(val); }
void CGraphFBXFreeParseContext( int32 context )
{
	delete GetContext(context);
}
int32 CGraphFBXGetLastErrorStrLen( int32 context )
{
	return (int32)GetContext(context)->m_errorMessage.length() + 1;
}
void CGraphFBXGetLastErrorStr( int32 context, charType* errorBuffer )
{
	memcpy( errorBuffer, GetContext(context)->m_errorMessage.c_str(), (GetContext(context)->m_errorMessage.length()+1) * sizeof( charType ) );
}
/// Smashes pivots into unified space.  Returns 0 on error, 1 otherwise.
void CGraphFBXLoadFile( int32 context, const charType* file )
{
	GetContext( context )->LoadFile( file );
}

//General properties
uint32 CGraphFBXGetNodeCount( int32 context )
{
	return (uint32)GetContext( context )->m_nodes.size()-1;
}
uint32 CGraphFBXGetMaterialCount( int32 context )
{
	return (uint32)GetContext( context )->m_materials.size()-1;
}
uint32 CGraphFBXGetTextureCount( int32 context )
{
	return (uint32)GetContext( context )->m_textures.size()-1;
}
uint32 CGraphFBXGetSceneGraphEdgeCount( int32 context )
{
	return (uint32)GetContext( context )->m_edges.size()-1;
}

inline uint32 GetPropertyList( Context* ctx, KFbxObject* obj )
{
	if ( obj != NULL )
		return ctx->AddObjectProperties( obj );
	return 0;
}

inline KFbxNode* GetNode( Context* ctx, uint32 index )
{
	if ( index < ctx->m_nodes.size() )
		return ctx->m_nodes[index+1];
	return NULL;
}
inline KFbxSurfaceMaterial* GetMaterial( Context* ctx, uint32 index )
{
	if ( index < ctx->m_materials.size() )
		return ctx->m_materials[index];
	return NULL;
}
inline KFbxTexture* GetTexture( Context* ctx, uint32 index )
{
	if ( index < ctx->m_textures.size() )
		return ctx->m_textures[index];
	return NULL;
}

inline KFbxLight* GetLight( Context* ctx, uint32 index )
{
	if ( index < ctx->m_lights.size() )
		return ctx->m_lights[index];
	return NULL;
}

inline KFbxCamera* GetCamera( Context* ctx, uint32 index )
{
	if ( index < ctx->m_cameras.size() )
		return ctx->m_cameras[index];
	return NULL;
}
inline KFbxMesh* GetMesh( Context* ctx, uint32 index )
{
	if ( index < ctx->m_meshes.size() )
		return ctx->m_meshes[index];
	return NULL;
}

uint32 CGraphFBXGetNodePropertyList( int32 context, uint32 index )
{
	Context* ctx = GetContext( context );
	KFbxObject* node = GetNode( ctx, index );
	return GetPropertyList( ctx, node );
}
uint32 CGraphFBXGetMaterialPropertyList( int32 context, uint32 index )
{
	Context* ctx = GetContext( context );
	KFbxObject* node = GetMaterial( ctx, index );
	return GetPropertyList( ctx, node );
}

uint32 CGraphFBXGetTexturePropertyList( int32 context, uint32 index )
{
	Context* ctx = GetContext( context );
	KFbxObject* node = GetTexture( ctx, index );
	return GetPropertyList( ctx, node );
}

uint32 CGraphFBXGetCameraPropertyList( int32 context, uint32 index )
{
	Context* ctx = GetContext( context );
	KFbxObject* node = GetCamera( ctx, index );
	return GetPropertyList( ctx, node );
}
uint32 CGraphFBXGetLightPropertyList( int32 context, uint32 index )
{
	Context* ctx = GetContext( context );
	KFbxObject* node = GetLight( ctx, index );
	return GetPropertyList( ctx, node );
}

uint32 CGraphFBXGetPropertyListCount( int32 context, uint32 propertyList )
{
	Context* ctx = GetContext( context );
	if ( propertyList == 0 || propertyList >= ctx->m_properties.size() )
		return 0;
	return (uint32)ctx->m_properties[propertyList].size();
}

static inline Property* GetProperty( int32 context, uint32 plist, uint32 prop )
{
	Context* ctx = GetContext( context );
	if ( plist == 0 || plist >= ctx->m_properties.size() )
		return 0;
	Context::TPropertyList& props = ctx->m_properties[plist];
	if ( prop > props.size() )
		return NULL;
	return props[prop];
}

uint32 CGraphFBXGetPropertyType( int32 context, uint32 propertyList, uint32 propIndex )
{
	Property* prop = GetProperty( context, propertyList, propIndex );
	if ( prop )
		return (uint32)prop->GetType();
	return 0;
}

uint32 CGraphFBXGetPropertyNameLength( int32 context, uint32 propertyList, uint32 propIndex )
{
	Property* prop = GetProperty( context, propertyList, propIndex );
	if ( prop && prop->GetName() )
		return (uint32)strlen(prop->GetName()) + 1;
	return 0;
}

void CGraphFBXGetPropertyName( int32 context, uint32 propertyList, uint32 propIndex, charType* buffer )
{
	Property* prop = GetProperty( context, propertyList, propIndex );
	if ( prop && prop->GetName() )
		strcpy(buffer, prop->GetName());
}

static inline KString GetPropertyString( int32 context, int32 propertyList, uint32 propIndex )
{
	KString value;
	FbxPropertyWrapper* prop = dynamic_cast< FbxPropertyWrapper* >( GetProperty( context, propertyList, propIndex ) );
	if ( prop && prop->GetType() == eSTRING )
		value = prop->GetProperty().Get( &value );
	return value;
}

uint32 CGraphFBXGetNamePropertyLength( int32 context, int32 propertyList, uint32 propIndex )
{
	return (uint32)GetPropertyString( context, propertyList, propIndex ).GetLen();
}

void CGraphFBXGetNameProperty( int32 context, int32 propertyList, uint32 propIndex, charType* buffer )
{
	strcpy( buffer, GetPropertyString( context, propertyList, propIndex ) );
}

void CGraphFBXGetDoubleProperty( int32 context, int32 propertyList, uint32 propIndex, double* buffer )
{
	Property* prop = GetProperty( context, propertyList, propIndex );
	FbxPropertyWrapper* propWrapper = dynamic_cast<FbxPropertyWrapper*>(prop);
	MatrixProperty* matrixProp;
	if ( prop == NULL )
		return;
	switch( prop->GetType() )
	{	
	case eUNIDENTIFIED:
		break;
	case eBOOL1:
		{
			bool value;
			value = propWrapper->GetProperty().Get( &value );
			*buffer = value ? 1 : 0;
		}
		break;
	case eENUM:
	case eINTEGER1:
		{
			int value;
			value = propWrapper->GetProperty().Get( &value );
			*buffer = value;
		}
		break;
	case eFLOAT1:
		{
			float value;
			value = propWrapper->GetProperty().Get( &value );
			*buffer = value;
		}
		break;
	case eDOUBLE1:
		{
			double value;
			value = propWrapper->GetProperty().Get( &value );
			*buffer = value;
		}
		break;
	case eDOUBLE2:
		{
			fbxDouble2 value;
			value = propWrapper->GetProperty().Get( &value );
			buffer[0] = value[0];
			buffer[1] = value[1];
		}
		break;
	case eDOUBLE3:
		{
			fbxDouble3 value;
			value = propWrapper->GetProperty().Get( &value );
			buffer[0] = value[0];
			buffer[1] = value[1];
			buffer[2] = value[2];
		}
		break;
	case eDOUBLE4:
		{
			fbxDouble4 value;
			value = propWrapper->GetProperty().Get( &value );
			buffer[0] = value[0];
			buffer[1] = value[1];
			buffer[2] = value[2];
			buffer[3] = value[3];
		}
		break;
	case eDOUBLE44:
		{
			fbxDouble44 value;
			if ( propWrapper == NULL )
			{
				matrixProp = dynamic_cast< MatrixProperty* >( prop );
				if ( matrixProp != NULL )
					value = matrixProp->GetMatrix();
			}
			else
				value = propWrapper->GetProperty().Get( &value );
			for ( int i = 0; i < 4; ++i )
				for ( int j = 0; j < 4; ++j )
					buffer[i*4+j] = value[i][j];
		}
		break;
	}
}
	
//ENodeAttachmentType
int32 CGraphFBXGetNodeAttachmentType( int32 context, uint32 idx )
{
	Context* ctx = GetContext(context);
	KFbxNode* node = GetNode( ctx, idx );
	if ( node != NULL && node->GetNodeAttribute() != NULL )
		return node->GetNodeAttribute()->GetAttributeType();
	return 0;
}

//Index into the geometry,camera,or light vectors.
uint32 CGraphFBXGetNodeAttachment( int32 context, uint32 idx )
{
	Context* ctx = GetContext(context);
	KFbxNode* node = GetNode( ctx, idx );
	KFbxMesh* mesh;
	KFbxCamera* camera;
	KFbxLight* light;
	if ( ( mesh = dynamic_cast< KFbxMesh* >( node->GetNodeAttribute() ) ) )
		return IndexOf( mesh, ctx->m_meshes );
	else if ( ( camera = dynamic_cast< KFbxCamera* >( node->GetNodeAttribute() ) ) )
		return IndexOf( camera, ctx->m_cameras );
	else if ( ( light = dynamic_cast< KFbxLight* >( node->GetNodeAttribute() ) ) )
		return IndexOf( light, ctx->m_lights );
	return 0;
}
//An edge is a pair of nodes, and thus each edge is *TWO*
//integers.  The length of the edge list must be 2*numEdges.es.
void CGraphFBXGetSceneGraphEdges( int32 context, uint32 index, uint32* edgeList)
{
	Context* ctx = GetContext( context );
	if ( index < ctx->m_edges.size() )
	{
		const Context::TEdge& edge = ctx->m_edges[index];
		edgeList[0] = edge.first;
		edgeList[1] = edge.second;
	}
}